package com.kelvin.backend.common.async;

import cn.hutool.core.util.ObjectUtil;
import com.kelvin.backend.common.exception.BaseException;
import com.kelvin.backend.common.util.ApplicationContextHelper;
import com.kelvin.backend.common.web.response.BaseCode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.concurrent.Future;

/**
 * <p>Title: AsyncService</p>
 * <p>Description: 异步调用通用方法</p>
 *
 * @author Kelvin.Chen
 * @datetime 2020/8/27 14:25
 */
@Component
public class AsyncService<T> {

    private final Logger log = LoggerFactory.getLogger(getClass());

    class Result<T> {
        T result;

        Result(T obj) {
            result = obj;
        }

        <T> T getResult() {
            return (T) result;
        }
    }

    @Async("asyncExecutor")
    public Future<T> getAsyncResult(String beanname, String methodname, Object... params) throws Exception {
        log.info("benanmae -> {}，methodname -> {}", beanname, methodname);

        if (ObjectUtil.isEmpty(beanname) || ObjectUtil.isEmpty(methodname)) {
            log.error("beanname【{}】或methodname【{}】不能为空", beanname, methodname);
            throw new BaseException(BaseCode.ERROR);
        }
        Object obj = ApplicationContextHelper.getBean(beanname);

        if (ObjectUtil.isEmpty(obj)) {
            log.error("未找到指定beanname【{}】的对象，请确保类名正确", beanname);
            throw new BaseException(BaseCode.ERROR);
        }

        Method[] methods = obj.getClass().getMethods();
        Method method = null;
        for (Method m : methods) {
            if (methodname.equals(m.getName())) {
                method = m;
            }
        }

        if (ObjectUtil.isEmpty(method)) {
            log.error("未找到指定methodname【{}】方法", methodname);
            throw new BaseException(BaseCode.ERROR);
        }

        Object result = method.invoke(obj, params);
        Result<Object> objectResult = new Result<>(result);
        return new AsyncResult<>(objectResult.getResult());
    }
}
